package org.NooLab.glue.components;

import java.io.File;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map.Entry;
import java.util.Observable;
import java.util.Observer;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import org.NooLab.glue.instances.InstanceFlavor;
import org.NooLab.glue.msgframe.BasicMessageParams;
import org.NooLab.glue.msgframe.DataDeliveryParamsPlus;
import org.NooLab.glue.msgframe.XMessage;
import org.NooLab.glue.msgframe.XRegistration;
import org.NooLab.glue.net.ComBoxIntf;
 
import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.files.DirectoryContent;
import org.NooLab.utilities.files.FileAccessControl;
 
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;
import org.NooLab.utilities.net.connex.FindFreePort;


/**
 * 
 * this may be used by any client instance to the MessageBoard to
 * register at the MessageBoard;
 * 
 * It is NOT part OF the MessageBoard, it just frees from boilerplate
 * 
 * TODO: routing to a callback-class , using a cb interface
 * 
 */
public class Registration implements Observer{

	// =================================

	// object references ..............

	MessageBoardFactory mbf ;
	MessageBoardFactoryProperties factoryProperties ;
	
	ComBoxIntf comBox ;
	
	XMessage xMessage ;
	XRegistration xRegistration ;
	/*
	UdpDataSender sender;
	UdpDataReceiver receiver ;
	 */
	DataSenderIntf sender;
	DataReceiverIntf receiver ;
	
	
	
	// main variables / properties ....
 
	String contentType = "*" ;
	
	int lastErrorCode=0;
	
	// constants ......................
	
	String registrationCatalogFilename = "xregistrations.pcat" ;

	// volatile variables .............
	
	int attemptNo;
	
	RegSender regSender ;
	int adjustedRetryCount=0;
	
	long messageboardId = -1;
	
	String entityName = "" ;
	String role="" ;
	
	int port = 0;
	int mbServicePort ;
	
	double connectionDelay=0.0;
	
	boolean instanceIsUnique=false ;
	boolean processCompleted=false ;
	String processCompletionReason = "";
	
	int linkRelation = -1;       
	int RelationItemID = -1 ; 
	int RelationGroupID = -1;		
	
	boolean stopWaitingEnforced=false;
	boolean isWaiting=false;
	
	// helper objects .................
	 
	XMessage xmsg ;
	
	FindFreePort freePort = new FindFreePort() ; 
	
	PrintLog out = new PrintLog(2,true);
	DFutils fileutil = new DFutils();
	DirectoryContent dir = new DirectoryContent();
	
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	public Registration( String name, String role, MessageBoardFactory mbf, InstanceFlavor flavor, int attemptNo ){
		
		entityName = name ;
		
		this.mbf = mbf ;
		this.role = role;
		this.attemptNo = attemptNo;
		
		if (flavor!=null){
			linkRelation = flavor.getLinkRelation() ;
			RelationItemID = flavor.getRelationItemID() ; 
			RelationGroupID = flavor.getRelationGroupID() ;
			contentType = flavor.getContentType() ;
			connectionDelay = flavor.getConnectionDelay() ;
			instanceIsUnique = flavor.getInstanceIsUnique() ;
		}
		  
		factoryProperties = mbf.getFactoryProperties() ;
		
		comBox = mbf.comBoxFactory.getComBox() ;
		
		xmsg = new XMessage(out) ;
	}
	
	public Registration( MessageBoardFactory mbf, String role){
		this.mbf = mbf ;
		this.role = role;
	}

	public Registration( MessageBoardFactory mbf ){
		this.mbf = mbf ;
		this.role = "";
	}
	
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	
	  
	/**
	 * 
	 * 
	 * 
	 */
	
	public int register( long instanceID , int attemptNo) throws Exception{
		
		int result = -1 ;
	 		String  xml,contenttype ;
		int regPortatMB=-1,p,pid, listenport = -1;
		 
		 
		if (mbf==null){
			return -3;
		}

		if (xMessage==null){
			xMessage = new XMessage(out);
		}

		contenttype = contentType ;
		
		
		pid = factoryProperties.getProtocolId() ;
		
		// we need this port stuff only for directly /& bidirectionally connecting protocols 
		if (pid >= MessageBoardFactoryProperties.protocolTCP ){
		
			listenport = getAppropriateListenPort() ;
		
			mbf.addReservedPort(listenport) ;
		
			// this also sets the variable port, which is the global listening port for protocols>=7
			if (getFreePortForSending()==false){
				return -3;
			}
		
			regPortatMB = mbf.getMsgPortforRegistration() ; // e.g. 7112
			
			
		} // direct connection protocols
		else{
			
			port = factoryProperties.getMsgPortAllInOne() ; // e.g. 7070
		}
		
		BasicMessageParams basics = new BasicMessageParams();

		basics.instanceID = instanceID;
		basics.instanceName = entityName;
		basics.roleOfSender = role ;
		
		DataDeliveryParamsPlus paramsplus = new DataDeliveryParamsPlus();
		 
		paramsplus.contentType = contenttype ;
		paramsplus.linkRelation =  linkRelation ;
		paramsplus.relationItemID = RelationItemID  ;
		paramsplus.relationGroupID = RelationGroupID  ;
		paramsplus.uniqueInstance = instanceIsUnique;
		paramsplus.alignedResults = factoryProperties.semiAlignedResultsBasketSize ;
		 
		paramsplus.ipAddressesStr = mbf.getIpAddressListStr() ; // retrieved during startup in mbf
		paramsplus.protocolID = mbf.protocolID ;
		paramsplus.timetolive = 1000*60*20; // TODO offer a parameter through the factory, default value on the board is sth like 15 min
		
		// TODO: add persistence mode !
		
		// this creates the role-specific message for the MessageBoard
		xml = xMessage.createRegistration(  basics ,// role, instanceID,  entityName , contenttype, 
											port, listenport,  connectionDelay,
											paramsplus ); // linkRelation , RelationItemID , RelationGroupID); 
		

		// mbf.
		// in case of http, we do need just 1 single explicit receiver for all the time! 
		// we create a dedicated receiver for registrations just for safety...
		
		// the receiver is an object, which is polling the server for news... 
		// long-term asynchronous state changes!
		// what does the comBox/Factory already know when we call it here ?
		receiver = comBox.createReceiver( port, mbf.getServicePort(), this, entityName );
	    
					if (receiver!=null){
						out.print(3, "... registration receiver has been defined for port : " + port);
					}

					if (receiver==null){
						xRegistration = null;
						return -10;
					}

		if (pid >= MessageBoardFactoryProperties.protocolTCP ){
			if (role.toLowerCase().contains("source")) {
				regPortatMB = factoryProperties.msgPortforRegistration;
			} else {
				regPortatMB = factoryProperties.msgPortforRegistration;
			}
		}else{
			// if (role.toLowerCase().contains("source")) {
			if (MessageBoardFactoryProperties.isSourceType(role)){
				regPortatMB = factoryProperties.msgPortforSources ;
			} else {
				regPortatMB = factoryProperties.msgPortforReceptors ;
			}
			
		}
		 
		
		// sending the message to the MessageBoard, it will get the registration directly as a response
		// it is relayed via callback to the method "" in class ""  ????
		// the comBox should know about our role! since we will address different resources
		sender = comBox.createSender( regPortatMB, mbf.getServicePort(),"register("+role+") for port "+listenport) ; 
		// creates the address:  http://localhost:7070/vp-1 ??? 
					if (sender==null){
						xRegistration = null;
						return -11;
					}

		
		// sending occurs as a consequence of construction, before we have to 
		// transfer / define / declare the Registration class as the Observer in order
		// to receive the replies...
		if (pid >= MessageBoardFactoryProperties.protocolTCP ){
			
			regSender = new RegSender(xml);
			
		} else {
			mbf.comBoxFactory.setObserver(this) ;
			// we need additional routing information to be attached as a prefix to the 
			// xml :  "/register"
			xml = xMessage.createRestletRoutingPrefix(xml, "/register", "put"); // new -> post will be used
			regSender = new RegSender(xml,1);
		}
		if (xRegistration==null){ 
			result = waitForRegistryProcess(regPortatMB);
		}
		return result;
	}
	
	public void update(Observable observedObj, Object objdata) {
		boolean hb;
		String repliedStr = "";
		
		repliedStr = (String)(objdata);
		                 
		if (repliedStr==null){
			return ;
		}
													   
		if ( (repliedStr.contains("reason=\"id"))  ){
			// out.printErr( 2,repliedStr);
		}
		// is it the register reply ?
		if ( (repliedStr!=null) && (repliedStr.contains("<registration>")) && (repliedStr.contains("</registration>"))){
			
												out.print( 4, "\n==============\nupdate in 'Registration' component received data for '"+ entityName +"' on port "+port+" :  ");
												out.print( 4, "     "+ repliedStr+"==============\n")  ;
			// stop listening to that port, remove the respective receiver process
			// releaseRegisteringReceiver() ; 
			
			// yes, -> dispatch it, it also sets : processCompleted=true;
													out.print( 5,"handlingRegistrationReply() A...");
			hb = handlingRegistrationReply( repliedStr ) ;
													out.print( 5,"handlingRegistrationReply() B...");
			try {		
				
				 
			} catch (Exception e) {
	
			}
		} else{
			if (repliedStr.length()==0){
				out.printErr(5, "reply string returning from MessageBoard was unexpectedly empty...") ;
			}else{
				out.print(6, "reply string returning from MessageBoard is xml: \n"+ repliedStr) ;
			}
		}
		out.print( 5,"----------- leaving update() in Registration --------------");
		
	}
	 
	private boolean handlingRegistrationReply( String rawXmlMsg){
		int r=0, port=-3;
		boolean rB=false;
		String originatorID;
		
		String[] rawIndicators ;
		
		
		try{
			
			originatorID = xmsg.getInfoFromProlog( rawXmlMsg, "origin", "id", ".");
			rawXmlMsg = xmsg.removePrologFromXml(rawXmlMsg) ;
			
			if ( (rawXmlMsg==null) || (rawXmlMsg.length() < 40) || (xmsg.isXML(rawXmlMsg)==false)){
				return false;
			}
			if ((rawXmlMsg.indexOf("<registration>")<0) && (rawXmlMsg.indexOf("<unregister")<0)){
				return false;
			}
			
			// ............
			
			  
			rawIndicators = new String[]{"<state code=\"0","<id value=","value=\"completed","<name value="} ;
			
			if ( assimilateRegistConfirmation( xmsg, rawXmlMsg ,rawIndicators) ){ 
				
				/*
				processCompleted = hb;
			
				//
				if (factoryProperties.getProtocolId() <= MessageBoardFactoryProperties.protocolFTP){
					hb=true;
				}else{
					
				}
				
				if (hb)
				*/
				{
					if ((regSender != null) && (regSender.timer != null)) {
						regSender.timer.cancel();
					}
					regSender = null;
				}
				
				 
				return true; 
			}
			
			
			rawIndicators = new String[]{"<state code=","<instanceID value=","value=\"denied" } ;
			if ( handlingRegistDenial( xmsg, rawXmlMsg ,rawIndicators) ){ return true; }
			
			
		}catch(Exception e){
			e.printStackTrace() ;
		}
														out.print(4,"handling Registration Reply: port assigned by the MessageBoard : "+port);
		// out.print(4,"handlingRegistrationReply() -> trying to define receiver for port : "+port);
		// receiver = new DataReceiver( port, mbf.getServicePort(), this, entityName );
		
		// r = receiver.testAssignedPort() ;
		// if r==0 the port is really assigned
		                                            if (r!=0){
														// out.print( 4,"result of attempt to assign port "+port+" ...   -> "+r);
		                                            }
	    /*
	    receiver.closePort() ;
	    receiver.deleteObservers() ;
	    processCompleted = true;
	    */
		                                            
	    r=0;
	    
	    try {
			// Thread.currentThread().join(100) ;
		} catch (Exception e) {
			  
		}finally{
		}
		return rB;
	}

	
	private boolean handlingRegistDenial( XMessage xmsg, String rawXmlMsg, String[] rawIndicators ){
		
		boolean result=false;
		long iid, preferredID, suggestedID ;
		String attemptValue, reason;
		
		result = xmsg.rawIndicatorsCheck( rawXmlMsg, rawIndicators);
		xmsg.setContentRoot("registration"); 
		
		if ( result ){
			preferredID = xmsg.getinstanceID(rawXmlMsg, "/instanceID") ;
			 
			suggestedID = xmsg.getSpecifiedValue(rawXmlMsg, "/state", "suggestion") ;
			
												out.print(2,"handling denial of registration, suggested new id: "+suggestedID+" ... ");
												out.print(3,"   ...denial msg is...");
												out.print(4,"   ...\n"+rawXmlMsg);
												
												// //registration/
			attemptValue = xmsg.getSpecifiedInfo( rawXmlMsg, "state", "value") ;
			reason = xmsg.getSpecifiedInfo(rawXmlMsg, "state", "reason").toLowerCase() ;
			
			if (reason.contentEquals("id")){
				if (attemptValue.contentEquals("denied")){
					// break the current registration raid, start a retry of the registration using the new suggestedID
					// we already INSIDE of the registration object !!
					
					xRegistration = null;

					// set a reason in order to suppress messages
					processCompletionReason = "restart::id";
					processCompleted= true; stopWaitingEnforced=true;
 					
					if (regSender!=null){
						regSender.requestStop();
					}
					while (isWaiting){
						delay(5);
					}
					
					delay(200);
					regSender = null ;
					
					try{
						
						adjustedRetryCount++; // should NOT be a property of the registration object,
											  // since this will be nullified, restarted
						// best would be to re-enter : MessageBoardParticipant.establishConnections()  
						//                parameters : XRegistration xreg, InstanceFlavor flavor , int numberOfConnectRetry         
						// if (adjustedRetryCount<=3) // just with changed value for desired ID
						{	// direct call does not work... register( suggestedID ) ;
							if (xRegistration==null)  {
								out.printErr(2, "calling restart with new suggested iid = "+suggestedID);
								mbf.getParticipantReference().getMrd().performSystemTask("restart::id="+suggestedID);
							}
							 
						}
						// else{  mbf.getParticipantReference().getMrd().performSystemTask("shutdown"); }
						
						
					}catch(Exception e){
						
					}
					
				} // attempt = "denied" ?
			} // reason = "id" ?
		}
		
		return result;
	}
	
	
	private int waitForRegistryProcess(int regPortatMB) throws Exception{
		int result = -1;
		boolean timedOut=false, hb;
		int z,exception=0;
		String str,remaddr, localAddresses, prnout;
		
		if (xRegistration != null){
			return 0;
		}
		// 
		//now the MessageBoard should start working
		 
				remaddr = factoryProperties.getCompleteRemoteAddress() ;
				
				str = factoryProperties.getRemoteAddressIPonly() ;
				
				if (str.length()>0){
					localAddresses = mbf.getIpAddressListStr();
					// is it remote ?
					str = xmsg.listContainsAny( str, localAddresses );
				
					if ((str.trim().length()<=0) && (remaddr.length()>0) && (remaddr.indexOf("localhost")<0)){
						remaddr = "remote -> "+remaddr;
					}
				}
				out.print(4, "...now the MessageBoard should start working ...");
				out.print(4, "   while the registration object is listening for the confirmation on port : "+ port);
				out.print(3, "...participant is listening for getting registered as "+this.role.toLowerCase()+"...");
				
				String transporter = MessageBoardFactoryProperties.decodeTransportLayerID( factoryProperties.getProtocolId() );
				if (factoryProperties.getProtocolId()>= MessageBoardFactoryProperties.protocolTCP ){
					transporter = transporter+":"+port;
				}
				
				str="";
				 
				if (remaddr.contains("localhost")==false){ 
					remaddr = "("+remaddr+")"; str="";
				}else{
					remaddr=""; str=" local";
				}
				prnout = "...waiting for completion of registration process at"+str+" MessageBoard "+remaddr ;
				// here we are in the client !
				
				if (remaddr.indexOf("remote")>=0){
					out.printErr(1, prnout);
				}else{
					out.print(1, prnout);
				}
				out.print(3, "transport layer is "+transporter+" ...");
		
		result = 0;
		stopWaitingEnforced=false; processCompletionReason="" ;
		// this time out is really critical, since the first time SAX is instantiated, it takes quite a long time
		int limit = 180; // 280 = max 28 seconds 
		isWaiting = false;
		z = 1;
		// "processCompleted" is set in update() => handlingRegistrationReply()
		while ((processCompleted == false) && (timedOut == false) &&(stopWaitingEnforced==false)) {
			isWaiting = true;
			if ((xRegistration != null) && (xRegistration.getInstanceID() > 0)){
				delay(50);
			}
			processCompleted = (xRegistration != null) && (xRegistration.getInstanceID() > 0);
	
			
			timedOut = (z > limit); // 100 = approx. 10 seconds
			if ((z > 2) && (z % 50 == 0)) {
				if ((z < 55) && (z > 5)) {
					// out.print(3,"");
				}
				 
	
				out.print(5, "obj xRegistration = " + xRegistration);
				out.print(4,".");
	
			}
			if (processCompleted == false) {
				delay(100);
			}
			if (processCompleted ) {
				delay(100);
			}
			
			z++;
		} // ->
		
		isWaiting = false;
		processCompleted = (xRegistration != null) && (xRegistration.getInstanceID() > 0);
		if ((processCompleted) && (xRegistration != null)) {
			String hs1="";
			adjustedRetryCount=0;
			if (factoryProperties.getProtocolId()>=MessageBoardFactoryProperties.protocolTCP ){
				hs1 = ", listening to port "+xRegistration.getClientListeningPort();
			}
			out.print(3, "\nlocation A: "+role+" registered with instance-ID = " + xRegistration.getInstanceID()+hs1 );
			
		}else{
			delay(140);
			z=z+0;
		}
		 
		if (xRegistration == null) {
			out.printErr(2, "\nPlease wait, registration has to be repeated...");
			out.printErr(3, "\nThe registration process has been interrupted/timed out and/or remains incomplete.");
			out.printErr(3, "Object xRegistration "+ xRegistration);
			out.printErr(3, "time out, steps      "+ z);
			out.printErr(3, "processCompleted     "+ processCompleted);
			out.printErr(3, "exception indicator  "+ exception);
 
			// we should check whether the remote address is accessible at all...
			// if not we do NOT throw an exception...
			if ((this.attemptNo==1) && (factoryProperties.getProtocolId()<MessageBoardFactoryProperties.protocolTCP )){
				int r = comBox.getFactory().glueUrlExists(port) ;
				if (r<0){
					return r;
				}
				
			}
			z=0;
			throw(new Exception("instance: "+role+", remote port: "+regPortatMB+", transport: "+ mbf.factoryProperties.protocolToString() )); 
		}
		if (xRegistration != null) {
			out.printErr(3, "The registration process is complete (XREG Id = " + xRegistration.getInstanceID() + ").\n\n");
			result = 0;
		}
	
		if (z > limit - 5) {
			out.print(3, "steps (x100ms) waited for completion of registration : " + z + "  ");
		}
	
		
		return result;
	}

	class RegSender implements Runnable{
		
		String xml;
		boolean timedOut;
		Thread regsendingThrd;
		int delayAsTimeOut = 14000, retryCount =5; 
		
		boolean enforcedStop=false;
		
		public Timer timer;
		
		
		public RegSender(String xml){
			this.xml = xml;
			init(0);
		}
		public RegSender(String xml, int retrycount) {
			this.xml = xml;
			init(retrycount);
		}
		
		
		private void init( int retrycount ){
			
			
			if (retrycount>0){
				retryCount = retrycount;
			}
			
			regsendingThrd = new Thread(this,"RegSender");
			regsendingThrd.start();
			
			// replace with IrTimer from utilities
			timer = new Timer(true) ;
			timer.schedule( new TimerTask() {
			    								public void run() {
			      											  out.print(5,"Requesting stop of registration thread <RegSender>...");
			      											  requestStop();
			      												}
			    							}, 
			    	        delayAsTimeOut);
			
		}
	
		@SuppressWarnings("static-access")
		private void regSendingHandler() {
	
			int z, r;
	
			out.print(5,"...sending request for registration to MessageBoard...");
	
			 
			
			processCompleted = false;
			z = 0;
			r = -1;
			while ((r < 0) && (z < retryCount) && (enforcedStop==false)) {
				r = 0;
				
				sender.send(xml);  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
				// in case of httpc, this will go to: HttpRestletSender::send()
				
				try {
					if (r < 0) {
						Thread.currentThread().sleep(200);
					}
					;
				} catch (Exception e) {
				}
				z++;
			}
			out.print(5, "Request for registration sent.");
		}
	
		@Override
		public void run() {
			regSendingHandler();
		}
		@SuppressWarnings("static-access")
		public void requestStop() {
			enforcedStop = true;
			retryCount = 1000;
			try {
				Thread.currentThread().sleep(200);
			} catch (InterruptedException e) {}
			
			try {
				// regsendingThrd.join();
			} catch (Exception e) {}
		}
		
		
	}

	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	
	
	private boolean assimilateRegistConfirmation( XMessage xmsg, String rawXmlMsg, String[] rawIndicators ){
		
		boolean result=false;
		
		result = xmsg.rawIndicatorsCheck( rawXmlMsg, rawIndicators);
		 
		if ( result ){
			
			if (mbf.protocolID >= MessageBoardFactoryProperties.protocolTCP) {
				port = (int) xMessage.getSpecifiedValue(rawXmlMsg,"//boardconfig/activeport", "value");
						
			}
	
												out.print(4,"handlingRegistrationReply() -> readConfirmedRegistration()... ");
												out.print(4,"... "+rawXmlMsg);
			xRegistration = xMessage.readConfirmedRegistration( rawXmlMsg ) ;
												out.print(4,"handlingRegistrationReply() -> releaseRegisteringReceiver()... ");
	
			
			receiver = mbf.comBoxFactory.getReceiver();
			// now we are ready to send the data, or the resourceLocator
	
			releaseRegisteringReceiver(1);
	
			// port = xRegistration.getClientListeningPort() ;
			 
			receiver = mbf.comBoxFactory.getReceiver();
			// messagereceptor ...
		
		}
		return result;
	}

	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	
	
	/**
	 * 
	 * we need unregistering, if we have a lot of registrants or many repeated registrations, such
	 * that the ports are running out...  in this case we have to perform an "unregister" of clients
	 * 
	 * @param registrationId
	 */
	@SuppressWarnings("static-access")
	public void unregister( XRegistration xRegistration ){
		
	
		int p,  regPortatMB, listenport=-1;
		long unregId;
		String name,xml="",xmlRestPrefix ;
		
		// this creates a special xml request,
		
		unregId    = xRegistration.getInstanceID(); 
		listenport = xRegistration.getClientListeningPort() ;
		name       = xRegistration.getNameOfApplicant() ;
		
		if (xMessage==null){
			xMessage = new XMessage(out);
		}
		xml = xMessage.createUnRegistration( unregId, name, xRegistration.getRole() ) ;
		
		if ( mbf.protocolID <= MessageBoardFactoryProperties.protocolFTP){
			 
			String resourcePath = "/register";
			
			xmlRestPrefix = xMessage.createRestletRoutingPrefix("", resourcePath, "delete");
			xml =  xmlRestPrefix + xml; // a prolog which will be removed by restlet framework connector
	
		}
		
		regPortatMB = mbf.getMsgPortforRegistration() ; // e.g. 7112
		
		
		// sender = new UdpDataSender( regPortatMB , mbf.getServicePort() ) ;
		if (comBox!=null){
			sender = comBox.createSender( regPortatMB, mbf.getServicePort(),"" );
			sender.send( xml ) ;
		}
		
		try{
			Thread.currentThread().sleep(200)  ;
		}catch(Exception e){
		}
		
		// release the socket port
		
		p = mbf.getReservedPorts().indexOf( listenport ) ;
		if (p>=0){
			mbf.getReservedPorts().remove(p) ;
		}
	}

	public void closeConnection(){
		releaseRegisteringReceiver(2) ;
	}
	 
	public String saveRegistration( String name, XRegistration xRegistration, int isClosing ){
		
		String path, filename ="" ;
		String suffixUID ="", _suffix="";
		
		
		
		try{
			
			suffixUID = xRegistration.getSuffixUID();
			
			if (suffixUID==null){
				suffixUID="" ;
			}
			
			if (suffixUID.length()>0){
				_suffix = "-"+suffixUID ; 
			}
			 
			path = mbf.getMessageBoardStorage().getHibernationPath(role) ;
			filename = "~xreg-"+name+_suffix +".ireg" ;
			
			filename = fileutil.createPath( path, filename) ;
			
			if (fileutil.fileexists(filename)==false){
				filename = "~xreg-"+name+".ireg" ;
				
				filename = fileutil.createPath( path, filename) ;
			}
			
			
			// write suffixUID to catalog, (lock file!!!)
			// suffixUID :: available
			// TODO catalog as property file
			
			fileutil.deleteFile(filename) ;
			
			fileutil.writeFileFromObject( filename, (Object)xRegistration);
			
			// save the catalog through RegistrationCatalog, 
			// it provides a locked access to prevent collisions
			( new RegistrationCatalog( path, registrationCatalogFilename) ).storeFileUID(suffixUID, isClosing);
			// registrationCatalogFilename ???
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return filename ;
	}

	public XRegistration loadRegistration( String name, boolean isUniqueInstance ){
		// uses name and serialID for filename
		XRegistration xreg=null;
		 
		String path,suffixUID,  filename="" ;
		Object obj;
		 
		
		try{
			path = mbf.getMessageBoardStorage().getHibernationPath( role ) ;
			
			if (isUniqueInstance==false){
				// the participant to load is NOT unique if suffixUID > ""
				// so we read any (first) available hibernated registration
				suffixUID = getNextAvailableRegistrationFileUID( path); // inside, we have a lock-controlled access to a catalog file
				// if there is no hib. registration any more, this suffixUID will be a newly create one
				 
				filename  = "~xreg-"+name+"-"+suffixUID+".ireg" ;
				
			}else{
				suffixUID = "" ;
				// here we have the case of a UNIQUE instance for the local machine,
				// since there is no UID, there is only 1 potential instance per base name
				// ( of course, it is still possible to create different names for the instances 
				//   through a factory-like arrangement)
				filename = "~xreg-"+name+".ireg" ;
				
			}
			
			// the file may exist due to 2 reasons: either it is unique, or it is 
			// in the catalog of registrations
			filename = fileutil.createPath( path, filename) ;
			if (fileutil.fileexists(filename)){
				// load the object file
				obj = fileutil.readObjectFromFile( filename );
				xreg = (XRegistration)obj ;
			}
			
			if ((xreg!=null) && (xreg.getSuffixUID().length()<3)){
				xreg.setSuffixUID(suffixUID) ;
			}
			// uniqueAdHocIdentifier ??
		}catch(Exception e){
			xreg = null;
			e.printStackTrace();
		}
		
		return xreg;
	}

	private boolean getFreePortForSending(){
		boolean rB=false;
		
		port = -1;
		
		freePort.setReservedPorts( mbf.getReservedPorts() );
		
		try {
			port = freePort.getFreePort(50001, 56000);
			
		} catch (IOException e) {			
			e.printStackTrace();
			port = -3;
		}
		
		if (port<0){
			port = freePort.anyFreePort();
		}
		
		rB = (port>0);
		
		return rB;
	}

	private int getAppropriateListenPort(){
		int  listenport = -1;
		
		while (listenport<0){
			
			try {
	
				listenport = freePort.getFreePort(7001, 11000);
				
				if ( (listenport==factoryProperties.msgPortforReceptors) ||
					 (listenport==factoryProperties.msgPortforSources) ||	
					 (listenport==factoryProperties.msgPortforRegistration) ||
					 (listenport==mbf.getServicePort() ) 
					 // || ( mbf.getReservedPorts().contains(listenport))
														 ){
					listenport = -3;
				}
			} catch (IOException e) {
				 
				System.err.println("Unable to open server in port range(7001-9000)");
				listenport = freePort.anyFreePort();
				System.err.println("next free port returned was : "+listenport);
			}
		}
		 
		
		return  listenport ;
	}

	@SuppressWarnings("static-access")
	private void delay(int millis){
		
		try {
			Thread.currentThread().sleep(millis);
		} catch (Exception e) {}	
	}

	private void releaseRegisteringReceiver(int marker){
											
		if (receiver!=null){
			out.print(4, "-- releaseRegisteringReceiver("+marker+") - closing the receiver serving port "+ receiver.getListenPort()) ;
			receiver.closePort();
			receiver = null;
		}
		// we do not need it anymore, so we release it 
		freePort.releaseFromReservation(port) ;
		out.print(4, "-- releaseRegisteringReceiver() -- Ok.   "); 
		
	}

	public void prepareErasureOfObject(){
		if (regSender!=null){
			regSender.requestStop();
		}
		
		stopWaitingEnforced = true;
		delay(120);
	}
	
	/**
	 * 
	 * this return a UID
	 * we use a catalog file
	 * 
	 * @return
	 */
	private String getNextAvailableRegistrationFileUID(String path){
		
		String fileUID = "" ;
		RegistrationCatalog regfetch;
		
		try{
			// regfetch = new RegistrationFetcher( path, "xregistrations.pcat"); // a properties file
			// fileUID = regfetch.getFileUID();
			fileUID = ( new RegistrationCatalog( path, registrationCatalogFilename) ).getFileUID();
		}catch(Exception e){
			
		}
		
		return  fileUID;
	}
	
	
	class RegistrationCatalog {
		
		String catalogFile , path, fileUID="";
			
		File file;
		FileAccessControl facc =null ;
		
		
		public RegistrationCatalog( String path, String catalogfile){
			
			catalogFile = catalogfile ;
			this.path = path;
			
		}
		
		public String getFileUID(){

			try{
				
				fileUID  = this.getNextAvailRegistrationFUID(path);
				
			}catch(Exception e){
			}

			return  fileUID;
		}
		
		private void openCatalog() throws Exception{
			
			
			catalogFile = fileutil.createPath(path, catalogFile) ;
 			if (fileutil.fileexists(catalogFile)==false){
				fileutil.writeFileSimple( catalogFile, "");
			} 
			
 			facc = new FileAccessControl( catalogFile, 250); // 250 = max waiting time
 			file = facc.getFile(); 
 			
 			if (file ==null){
 				out.print(2, "access to catalog file denied...");
 				
 				throw (new Exception("")) ;
 			}
 			
 			
		}
		
		 
		public void storeFileUID( String suffixUID, int avail) throws Exception{
			
			Properties assignedFUIDs;
			 
			
			// 
			openCatalog(); // create facc-lock and file variable
			
			assignedFUIDs = fileutil.readPropertiesByFile( file ) ;
			
			if (avail<=0){
				assignedFUIDs.put(suffixUID, "used") ;
			}else{
				assignedFUIDs.put(suffixUID, "available") ;
			}
			
			fileutil.writePropertiesByFile( file, assignedFUIDs) ;
			
			
			if ((file != null) && (facc!=null)){
				facc.close(); // this releases the lock
			}
		}
		
		
		private String getNextAvailRegistrationFUID(String path) throws Exception{
			
			String fuid = "" ;
			ArrayList<String> xrFileNames;
			Properties assignedFUIDs;
			 
			
			try{
				
				openCatalog();
	 			 
	 			// catalogXmlStr = fileutil.readFile2String( file );
	 			// load directly to properties object
	 			assignedFUIDs = fileutil.readPropertiesByFile( file ) ;
	 			
				// is there anything in this directory
				xrFileNames = dir.getFileList("~xreg-", ".ireg", path) ;
				
				if (xrFileNames.size()>0){
					
					fuid = matchFuidsWithFiles(assignedFUIDs, xrFileNames) ;
					// for the returned fuid > "" , iff a xreg file exists containing this uid, AND if it is available
					
					if (fuid.length()==0){
						fuid = GUID.randomvalue() ;
					}else{
						// set this entry to not available ("used")
						// the file is still blocked
						assignedFUIDs.put(fuid, "used") ;
						fileutil.writePropertiesByFile( file, assignedFUIDs) ;
						
					}
				}else{
					fuid = GUID.randomvalue() ;
				}
				
				
			}catch(Exception e){
				e.printStackTrace();
			}finally{
				if ((file != null) && (facc!=null)){
					facc.close(); // this releases the lock
				}
			}
			
			return fuid;
		}
 
	}
	
	/**
	 * 
	 * for the returned fuid > "" , iff a xreg file exists containing this uid, AND if it is available
	 * 
	 * @param assignedFUIDs
	 * @param xrFileNames
	 * @return
	 */
	private String matchFuidsWithFiles( Properties assignedFUIDs, ArrayList<String> xrFileNames){
		
		String fuid="", propsfuid ,fnstr, flag ;
		int p;
		
		
		// the properties contain a list of pairs like {1458553c-c7ba-4e34-a2ef-fb48166f36ae=available}
	 
        for (Entry<Object, Object> entry : assignedFUIDs.entrySet()) {
        	// System.out.println( "Key : " + entry.getKey()+ " Value : " + entry.getValue());
        	propsfuid = (String)entry.getKey();
        	
        	flag = (String)entry.getValue();
        	
        	if (flag.contains("avail")){
				for (int i = 0; i < xrFileNames.size(); i++) {
					fnstr = xrFileNames.get(i);
					p = fnstr.lastIndexOf("/");
					if (p > 0) {
						fnstr = fnstr.substring(p + 1, fnstr.length());
					}
					if ((propsfuid.length() > 0) && (fnstr.contains(propsfuid))) {
						fuid = propsfuid;
						break;
					}
				}
        	} // is the entry avail ?
        }
		
		return fuid;
	}
	
	
	
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	
	
	public DataReceiverIntf getReceiver() {
		return receiver;
	}

	public long getIdFromMessageBoard(){
		messageboardId = -3;
		
		if (xRegistration!=null){
			messageboardId = xRegistration.getInstanceID() ;
		}
		
		return messageboardId;
	}

	public XRegistration getRegistrationObject(){
		
		return xRegistration;
	}

	public void setContentType( String contenttype) {
		
		contentType = contenttype ;
	}

	/**
	 * 
	 * in case of non-unique registrations, return the number of hibernated registrations
	 * which are available
	 *  - onlyRemaining=true  ->  still available
	 *  - onlyRemaining=false ->  total
	 * 
	 * @param onlyRemaining
	 * @return
	 */
	public int getNumberOfAvailableRegistrations( boolean onlyRemaining ){
		
		return 0;
	}
	
	public String getRole() {
		return role;
	}

	// ------------------------------------------------------------------------
	public int getLinkRelation() {
		return linkRelation;
	}

	public void setLinkRelation(int linkRelation) {
		this.linkRelation = linkRelation;
	}

	public int getRelationItemID() {
		return RelationItemID;
	}

	public void setRelationItemID(int relationItemID) {
		RelationItemID = relationItemID;
	}

	public int getRelationGroupID() {
		return RelationGroupID;
	}

	public void setRelationGroupID(int relationGroupID) {
		RelationGroupID = relationGroupID;
	}

	public String getContentType() {
		return contentType;
	}

	public int getLastErrorCode() {
		return lastErrorCode;
	}
	
	
	
}
